import {
  CSDashLine,
  CSLine,
  MathTool,
  ParameterObjectType,
  RENDERORDER,
  SketchFrame,
} from '@amcax/base';
import { ctx2 } from '@app-cad/module';
import {
  Mesh,
  Object3D,
  Vector3,
  OrthographicCamera,
  PerspectiveCamera,
  Shape,
  Vector2tuple,
} from 'three-legacy';
import {
  DimCallback,
  DimColors,
  extendsLine,
  FunctionOnBeforeRender,
  LineKey,
} from './types';
import { ArrowMesh, ArrowMeshAttrs } from './dimTools/Arrow';
import { DimCss2dInputLable } from './dimTools/label';
import { debounce } from 'lodash';
import { sketchEventCenter } from '@app-cad/environments/nextcad/events/SketchEventCenter';
import {
  calculatePoint,
  distanceToLine,
  getAngle,
  isEffectiveValue,
} from '@app-cad/common/utils';
import { ArcParams, Setting } from '../../CmdBase';
import { DimInfo } from '../cmd-render-pick/type';
import { sketchStore } from '@app-base/common/utils/sketchTools/useSketchStore';

export default abstract class DimBase extends Mesh {
  public scene: Object3D;
  public dimLabel: DimCss2dInputLable; // 标注
  public isDisabled: boolean = false; // 是否禁用
  public lines: { [key in LineKey]?: CSDashLine } = {};
  public mouseXY: Vector3;
  private lineWidth: number = 1;
  private sketchFrame: SketchFrame;
  private arrowMesh: ArrowMesh[] = []; // 箭头;
  // 圆弧的补线
  private color: number;
  private zoom: number;
  private widthHeight: {
    width: number;
    height: number;
  };
  public abstract update(attrs): void;
  public userData: Partial<DimInfo> = { objects: [] };
  private _debounce;
  zoomUpdate() {
    return null;
  }
  constructor() {
    super();
    const { frame } = sketchEventCenter().getSketchInfo();
    this.sketchFrame = new SketchFrame(frame);
    this.onBeforeRender = this.makeOnBeforeRender(this.onBeforeRender);
    this._debounce = debounce(this.zoomUpdate, 50); // 防抖
  }
  setUserData(value: DimInfo) {
    for (const key in value) {
      if (!value[key]) {
        delete value[key];
      }
    }
    this.userData = Object.assign(this.userData, value);
  }
  getSketchFrame() {
    return this.sketchFrame;
  }
  // 允许click激活
  labelFoucs() {
    this.dimLabel?.clickFocus();
  }
  // 不允许click激活
  unClickFocus() {
    this.dimLabel?.unClickFocus();
  }
  focus() {
    this.dimLabel?.focus();
  }
  blur() {
    this.dimLabel?.blur();
  }
  // 偏移
  offset(offset: Vector3) {
    this.mouseXY = this.mouseXY.add(offset);
    this.update({ labelPos: this.mouseXY });
  }
  // 取消高亮
  setNormalColor() {
    const { line } = DimColors.normal;
    this.setLineDash(false);
    if (this.isDisabled) {
      this.setRenderOrder(RENDERORDER.LINE - 1);
      this.setDisable();
    } else {
      this.dimLabel?.setHover(false);
      this.dimLabel?.setSelected(false);
      this.setLineColor(line);
      this.setArrowColor(line);
      this.setRenderOrder(RENDERORDER.LINE);
      this.color = line;
    }
    this.setLineWidth(1);
  }
  selected() {
    const { line } = DimColors.select;
    this.setLineColor(line);
    this.setArrowColor(line);
    this.dimLabel?.setSelected(true);
    this.color = line;
    this.setRenderOrder(RENDERORDER.LINE_S);
    this.setLineWidth(2);
  }
  getArcVertexs(params: ArcParams) {
    const shape = new Shape().absarc(
      params.x,
      params.y,
      params.radius,
      params.startAngle,
      params.endAngle,
      params.clockWise,
    );
    const points = shape.getPoints(25);
    if (points.length < 51) {
      return [];
    }
    return points.map((item) => new Vector3(item.x, item.y, 0));
  }
  // 禁用
  setDisable() {
    this.setDisableColor();
    this.dimLabel?.setDisable(true);
    this.color = DimColors.disable.line;
    this.isDisabled = true;
    this.setRenderOrder(RENDERORDER.LINE - 1);
  }
  // 设置禁止颜色
  setDisableColor() {
    this.setLineColor(DimColors.disable.line);
    this.setArrowColor(DimColors.disable.line);
  }
  unDisable() {
    if (this.disableIfCurvesFixed) {
      return;
    }
    this.dimLabel?.setDisable(false);
    this.isDisabled = false;
    this.setNormalColor();
  }
  // 高亮
  highlight() {
    const { line } = DimColors.hover;
    this.setLineColor(line);
    this.setArrowColor(line);
    this.color = line;
    this.dimLabel?.setHover(true);
    this.setRenderOrder(RENDERORDER.LINE_H);
    this.setLineWidth(2);
  }
  // 预览
  preview() {
    this.setLineDash(true);
    this.setLineColor(DimColors.preview.line);
    this.setArrowColor(DimColors.preview.line);
  }
  setLineFn<T>(fun: (line: CSLine) => T, unKey?: LineKey[]) {
    for (const key in this.lines) {
      if (!unKey?.includes(key as LineKey)) {
        fun(this.lines[key]);
      }
    }
  }
  // 设置线段颜色
  setLineColor(color: number) {
    this.setLineFn((line) => line?.setColor(color));
  }
  setRenderOrder(v) {
    this.setLineFn((line) => line?.setRenderOrder(v));
  }
  setLineDash(v) {
    this.setLineFn((line) => line?.setDashed(v), ['arcLine']);
  }
  setLineWidth(v) {
    this.lineWidth = v;
    this.setLineFn((line) => line?.setLineWidth(v));
  }
  setLineVisible(v: boolean) {
    this.visible = v;
    if (this.dimLabel?.object?.visible) {
      this.dimLabel.object.visible = v;
    }
  }
  hidden() {
    this.setLineVisible(false);
  }
  show() {
    this.setLineVisible(true);
  }
  removeLine(keys: LineKey[]) {
    keys.forEach((key) => {
      this.remove(this.lines[key]);
      this.lines[key]?.destroy();
      this.lines[key] = null;
    });
  }
  // 设置箭头颜色
  setArrowColor(color: number) {
    this.arrowMesh.forEach((item) => {
      item.setColor(color);
    });
  }
  addTo(scene: Object3D) {
    this.scene = scene;
    this.scene.add(this);
  }
  destroy() {
    this.dimLabel?.destroy();
    if (this.scene) {
      this.scene.remove(this);
    }
  }
  // 设置箭头
  setArrow(
    points?: Vector3[] /** 本地坐标 */,
    arrowVecs?: Vector3[],
    type: 'vector' | 'point' = 'point',
  ) {
    let isReversTaking = false;
    if (points.length === 2 && points[0] && points[1]) {
      const len = points[0].distanceTo(points[1]);
      const len2 = 50 / ctx2.getCameraZoom();
      if (len < len2) {
        isReversTaking = true;
      }
    }
    this.arrowMesh.forEach((item) => {
      item.destroy();
    });
    this.arrowMesh = [];
    const arr = type === 'vector' ? arrowVecs : points;
    arr.forEach((item, index) => {
      const param: ArrowMeshAttrs = {
        id: 'arrow',
        sketchFrame: this.sketchFrame,
        position: points[index],
        isReversTaking,
        color: this.color,
      };
      if (type === 'vector') {
        param.arrowVec = item;
      } else {
        param.points = index === 0 ? points : points.reverse();
      }
      const arrow = new ArrowMesh(param);
      this.arrowMesh.push(arrow);
      arrow.addTo(this);
    });
  }
  // 更新箭头
  updateArrow(
    points?: Vector3[],
    arrowVecs?: Vector3[],
    type: 'vector' | 'point' = 'point',
  ) {
    let isReversTaking = false;
    if (points.length === 2 && points[0] && points[1]) {
      const len = points[0].distanceTo(points[1]);
      const len2 = 50 / ctx2.getCameraZoom();
      if (len < len2) {
        isReversTaking = true;
      }
    }
    const arr = type === 'vector' ? arrowVecs : points;
    const arrlen = arr.length;
    const arrowMeshLen = this.arrowMesh.length;
    if (arrlen < arrowMeshLen) {
      this.arrowMesh.pop()?.destroy();
    } else if (arrlen > arrowMeshLen) {
      this.setArrow(points, arrowVecs, type);
    }
    if (!(arrlen > arrowMeshLen)) {
      arr.forEach((item, index) => {
        const param: ArrowMeshAttrs = {
          position: points[index],
          isReversTaking,
        };
        if (type === 'vector') {
          param.arrowVec = item;
        } else {
          param.points = index === 0 ? points : points.reverse();
        }
        this.arrowMesh[index].update(param);
      });
    }
  }
  createUpdateLabel<
    T extends {
      callback?: DimCallback;
      curveIds?: string[];
      param?: string;
      id?: string;
    },
  >(
    attrs: T,
    labelPos: Vector3,
    text: number,
    inputType = ParameterObjectType.Real,
    multiple?: number,
    prefix?: string,
  ) {
    if (this.dimLabel) {
      this.dimLabel.update({
        pos: labelPos,
        callback: attrs?.callback,
        text,
        param: attrs?.param,
      });
    } else if (attrs) {
      this.dimLabel = new DimCss2dInputLable({
        text,
        textParam: attrs?.param,
        position: labelPos,
        callback: attrs?.callback,
        id: attrs?.curveIds?.join(','),
        sketchFrame: this.sketchFrame,
        inputType,
        multiple,
        prefix,
        allowExpression: attrs.id != null,
      });
      this.dimLabel.addTo(this);
    }
  }
  // 修改标注的值失败后，需要重置标注的值
  resetDimLabel(value?: number, param?: string) {
    this.dimLabel.update({
      text: value ?? this.userData?.value,
      param: param ?? this.userData?.param,
    });
  }
  createUpdateLine(key: LineKey, points: Vector3[], setting: Setting = {}) {
    const vertexs = [];
    points?.forEach((item) => {
      if (isEffectiveValue(item)) {
        vertexs.push(this.sketchFrame.toWorld(item));
      }
    });
    if (vertexs?.length < 2) {
      return;
    }
    if (this.lines[key]) {
      this.lines[key].update({ vertexs });
      if (setting?.color) {
        this.lines[key].setColor(setting.color);
      }
    } else {
      this.lines[key] = new CSDashLine({
        vertexs,
        lineWidth: this.lineWidth,
        color: this.color || DimColors.normal.line,
        dashSize: 6,
        gapSize: 4,
        dashed: false,
        depthTest: false, // 不进行深度测试
        ...setting,
      });
      this.add(this.lines[key]);
    }
  }
  getVal() {
    return this.dimLabel.getText();
  }
  // 获取垂直距离
  getVerticalDis([p, vec, vecVer]) {
    return distanceToLine<Vector3>(this.mouseXY, {
      start: p,
      vec,
      normal: vecVer,
    });
  }
  // 计算延长或者缩短距离
  computeExtendLine() {}
  // 延长线段的函数
  extendLine(
    [point2, point1]: [Vector3, Vector3],
    status: 'plus' | 'minus' = 'plus',
    type: 'line' | 'arc' = 'line',
  ) {
    let extendsVal = null;
    const zoom = ctx2.getCameraZoom();
    const vec = new Vector3().subVectors(point2, point1).normalize();
    if (!point2 || !point1) {
      return [];
    }
    if (status === 'minus' || type === 'arc') {
      const widthHeight = this.dimLabel?.getLabelWH();
      const upAq = this.getUpAquadrant();
      const hKey = [1, 5, 4, 8].includes(upAq) ? 'width' : 'height';
      const wKey = [1, 5, 4, 8].includes(upAq) ? 'height' : 'width';
      const height = widthHeight?.[hKey] || this.widthHeight?.[hKey] || 0;
      const width = widthHeight?.[wKey] || this.widthHeight?.[wKey] || 0;
      const angle = vec.angleTo(new Vector3(1, 0, 0));
      extendsVal =
        Math.abs(Math.cos(angle)) * (width / 2) +
        Math.abs(Math.sin(angle)) * (height / 2);
      if (widthHeight?.width && widthHeight?.height) {
        this.widthHeight = widthHeight;
      }
    }
    const length =
      point2.distanceTo(point1) +
      ((extendsVal || extendsLine) / zoom) * (status === 'plus' ? 1 : -1);
    const point3 = calculatePoint(vec, length, point1);
    return [point1, point3];
  }
  getUpAquadrant() {
    const hDir = new Vector3(...sketchEventCenter().getSketchInfo().frame[2]);
    const cameraUp = ctx2.viewer.sceneSetup.camera.up.normalize();
    const xAxisAngle = MathTool.fixNumber(
      MathTool.radToDeg(getAngle(cameraUp, new Vector3(0, 0, 0), hDir)),
      0,
    );
    if (0 < xAxisAngle && xAxisAngle < 45) {
      return 1; // '第一象限'
    } else if (45 <= xAxisAngle && xAxisAngle <= 90) {
      return 2; // '第二象限'
    } else if (90 < xAxisAngle && xAxisAngle < 135) {
      return 3; //'第三象限'
    } else if (135 <= xAxisAngle && xAxisAngle <= 180) {
      return 4; //'第四象限'
    } else if (180 < xAxisAngle && xAxisAngle < 225) {
      return 5; //'第五象限'
    } else if (225 <= xAxisAngle && xAxisAngle <= 270) {
      return 6; //'第六象限'
    } else if (270 < xAxisAngle && xAxisAngle < 315) {
      return 7; //'第七象限'
    } else {
      return 8; //'第八象限'
    }
  }
  private makeOnBeforeRender(
    parentOnBeforeRender: FunctionOnBeforeRender,
  ): FunctionOnBeforeRender {
    return (renderer, scene, camera, geometry, material, group) => {
      // 调用父类的onBeforeRender，重新计算分辨率
      parentOnBeforeRender?.call(
        this,
        renderer,
        scene,
        camera,
        geometry,
        material,
        group,
      );
      if (
        camera instanceof PerspectiveCamera ||
        camera instanceof OrthographicCamera
      ) {
        if (this.zoom !== camera.zoom) {
          this.zoom = camera.zoom;
          this._debounce(this.zoom);
        }
      }
    };
  }
  setV2ToV3(v2: Vector2tuple | Vector3) {
    return v2 instanceof Vector3 ? v2 : new Vector3(v2[0], v2[1], 0);
  }
  defaultDimPos(num = 80) {
    return num / ctx2.getCameraZoom();
  }
  // 获取垂直水平向量
  getVec(p1: Vector3, p2: Vector3) {
    if (p1.equals(p2)) {
      const vec = p1.clone().normalize();
      return { vec: vec, vecVer: new Vector3(vec.y, -vec.x, 0), isEqual: true };
    } else {
      const vec = new Vector3().subVectors(p1, p2).normalize();
      // 垂直向量
      const vecVer = new Vector3(vec.y, -vec.x, 0);
      return { vec, vecVer, isEqual: false };
    }
  }
  abstract getPosLv(): Vector2tuple[];

  // 当前标注的相关curve是否全部是固定（由投影/相交产生的）对象
  disableIfCurvesFixed = false;

  // 按curveID校验，是否全部为固定（由投影/相交产生的）对象
  updateDisableIfCurvesFixed(curveIds: string[]) {
    const fixedCurves =
      sketchStore
        .getState()
        ?.curves?.filter(
          (c) => c.typeof3d === 'intersect' || c.typeof3d === 'project',
        ) ?? [];

    const fixedCurvesRelatives = fixedCurves.flatMap((c) => c.relative ?? []);

    const allFixedIds = [
      ...fixedCurvesRelatives,
      ...fixedCurves.map((c) => c.id),
    ];

    this.disableIfCurvesFixed =
      curveIds != null &&
      curveIds.every((curveId) => {
        return allFixedIds.includes(curveId);
      });

    if (this.disableIfCurvesFixed) {
      this.setDisable();
    }
  }
}
